#ifndef HASH_H_
#define HASH_H_

#include"MD5.h"

#ifndef NULL
#define NULL ((void*)0)
#define NULL_DEFINED true
#endif

#define hashsize(n) (1U << (n))
#define hashmask(n) (hashsize(n) -1)

#define mix(a,b,c) \
 { \
   a -= b; a -= c; a ^= ( c >> 13 ); \
   b -= c; b -= a; b ^= ( a << 8 ); \
   c -= a; c -= b; c ^= ( b >> 13 ); \
   a -= b; a -= c; a ^= ( c >> 12 ); \
   b -= c; b -= a; b ^= ( a << 16 ); \
   c -= a; c -= b; c ^= ( b >> 5 ); \
   a -= b; a -= c; a ^= ( c >> 3 ); \
   b -= c; b -= a; b ^= ( a << 10 ); \
   c -= a; c -= b; c ^= ( b >> 15 ); \
 }

static unsigned int seed = 3215865487;

template <class T>
void srand(T z)
{
	seed = ((unsigned int)z) & 0x7fffffffU;
}

unsigned int rand()
{
	seed = (seed * 1103515245U + 12345U) & 0x7fffffffU;
    return seed;
}

unsigned int RitchieHash(const char *s)
{
	//destructive
	unsigned int h = 0;
	while(*s)
		h = 31*h + (*s++);
	return h;
}

unsigned int AdditiveHash(const char *a, int n)
{
	//non destructive
	const char *p = a;
	unsigned int h = 0;
	
	for(int i = 0; i < n; i++)
		h += p[i];
	
	return h;
}

unsigned int XORHash(const char *a, int n)
{
	//non destructive
	const char *p = a;
	unsigned int h = 0;
	
	for(int i = 0; i < n; i++)
		h ^= p[i];
	
	return h;
}

unsigned int RotatingHash(const char *a, int n)
{
	//non destructive
	const char *p = a;
	unsigned int h = 0;
	
	for(int i = 0; i < n; i++)
		h ^= (h << 4) ^ ( h >> 28) ^ p[i];

	return h;
}

unsigned int BernstienHash(const char *a, int n)
{
	const char *p = a;
	unsigned int h = 0;

	for(int i = 0; i < n ; i++)
		h = 33 * h + p[i];

	return h;
}

unsigned int ModBernstienHash(const char *a, int n)
{
	const char *p = a;
	unsigned int h = 0;

	for(int i = 0; i < n ; i++)
		h = 33 * h + p[i];

	return h;
}

unsigned int ShiftADDXORHash(const char *a, int n)
{
	const  char *p = a;
	unsigned h = 0;

	for(int i = 0; i < n ; i++)
		h ^= (h << 5) + ( h >> 2 ) + p[i];  

	return h;
}

unsigned int FNVHash(const char *a, int n)
{
	const  char *p = a;
	unsigned h = 2166136261;
	for(int i = 0; i < n ; i++)
		h = (h * 1677619)%1000000000^p[i];  
	return (unsigned)h;
}

unsigned int ELFHash(const char *a, int n)
{
	const char *p = a;
	unsigned int h = 0,g;
	for(int i = 0; i < n; i++)
	{
		h = ( h << 4) + p[i];
		g = h & 0xf0000000L;

		if( g != 0)
			h ^= g >> 24;
		h = ~g;
	}
	return h;
}

unsigned int JenkinHash(const char *k, int n,int i)
{
	const char *a = k;
	unsigned int d,b;
	unsigned int c = (unsigned)i;
	unsigned int len = (unsigned)n;

	d = b = 0x9e3779b9;

	while ( len >= 12 ) {
		a += ( a[0] + ( (unsigned)a[1] << 8 ) 
			+ ( (unsigned)a[2] << 16 )
			+ ( (unsigned)a[3] << 24 ) );
		b += ( a[4] + ( (unsigned)a[5] << 8 ) 
			+ ( (unsigned)a[6] << 16 )
			+ ( (unsigned)a[7] << 24 ) );
		c += ( a[8] + ( (unsigned)a[9] << 8 ) 
			+ ( (unsigned)a[10] << 16 )
			+ ( (unsigned)a[11] << 24 ) );
 
		mix ( d, b, c );
 
		a += 12;
		len -= 12;
	}

	c += n;

	switch ( len ) {
		case 11: c += ( (unsigned)a[10] << 24 );
		case 10: c += ( (unsigned)a[9] << 16 );
		case 9 : c += ( (unsigned)a[8] << 8 );
    
		case 8 : b += ( (unsigned)a[7] << 24 );
		case 7 : b += ( (unsigned)a[6] << 16 );
		case 6 : b += ( (unsigned)a[5] << 8 );
    
		case 5 : b += a[4];
		case 4 : a += ( (unsigned)a[3] << 24 );
		case 3 : a += ( (unsigned)a[2] << 16 );
    
		case 2 : a += ( (unsigned)a[1] << 8 );
		case 1 : a += a[0];
    }

	mix(d,b,c);
	
	return c;
}

unsigned int KenefakeHash(unsigned char *a, int n)
{
	unsigned int randSeed = FNVHash((const char*)a,n);
	srand(randSeed);

	//random value table 
	const unsigned int tn = 1 << sizeof(a[0]);
	unsigned char tab[tn];
	for(int i = 0; i < tn; i++)
		tab[i] = (unsigned char)rand();
	
	unsigned char *p = a;
	unsigned int h = 16777551;

	for(int i = 0 ; i < n ; i++)
		h = ~(!(h << 1 | h >> 31) ^ tab[p[i]] & p[i])^rand();

	return h;
}

#undef hashmask
#undef hashsize
#undef mix
#undef NULL

#endif